﻿using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;

namespace GBricks.Collections
{
  partial class Comparers
  {
    public static IComparer<T> ThenBy<T>(this IComparer<T> first, IComparer<T> second) {
      Argument.NotNull(first, "first");
      Argument.NotNull(second, "second");
      Contract.Ensures(Contract.Result<IComparer<T>>() != null);
      return new ThenByComparer<T>(first, second);
    }

    public static IComparer<T> ThenByDescending<T>(this IComparer<T> first, IComparer<T> second) {
      Argument.NotNull(first, "first");
      Argument.NotNull(second, "second");
      Contract.Ensures(Contract.Result<IComparer<T>>() != null);
      var thenBy = ThenBy(first, second);
      return thenBy.Reverse();
    }

    [Serializable]
    private sealed class ThenByComparer<T> : Comparer<T>
    {
      public ThenByComparer(IComparer<T> first, IComparer<T> second) {
        Argument.NotNull(first, "first");
        Argument.NotNull(second, "second");

        First = first;
        Second = second;
      }

      private IComparer<T> First { get; set; }
      private IComparer<T> Second { get; set; }

      [ContractInvariantMethod]
      private void Invariant() {
        Contract.Invariant(First != null);
        Contract.Invariant(Second != null);
      }

      public override int Compare(T x, T y) {
        var value = First.Compare(x, y);
        if(value == 0) {
          return Second.Compare(x, y);
        }//if
        return value;
      }

      public override bool Equals(object obj) {
        var other = obj as ThenByComparer<T>;
        return other != null && Equals(other.First, First) && Equals(other.Second, Second);
      }

      public override int GetHashCode() {
        return HashCode.Combine(First.GetHashCode(), Second.GetHashCode());
      }
    }
  }
}
